#include "Platforms.h"
#include "Timer.h"
#include "ScenesManager.h"
#include "ResourcesManager.h"
#include "FileManager.h"
#include "InputManager.h"
#include "Framebuffer.h"
#include "ScreenSolution.h"
#include <math.h>


#ifdef _WIN32
#include "ESContext.h"
#include <vld.h>
#else
#ifdef _ANDROID
#include <jni.h>		
#endif
#endif

// FILE MANAGER
FileManager* fm = NULL;
// INPUT MANAGER
InputManager* im = NULL;
// SCENES MANAGER
ScenesManager* sceManager = NULL;
// RESOURCES MANAGER
ResourcesManager* resManager = NULL;

// FRAME BUFFER
Framebuffer* framebuffer;

union ColorDataConverter
{
    int iValue;
    struct
	{
		unsigned char r, g, b, a;
	};
};
ColorDataConverter unionColor;

void Init()
{
	//
	im = InputManager::GetInstance();

	//
	fm = FileManager::GetInstance();
	#ifdef _ANDROID
		fm->Init("sdcard/Resources/");
	#else
	#ifdef _WIN32
		fm->Init("../Resources/");
	#endif
	#endif

	// init resources manager
	resManager = ResourcesManager::GetInstance();
	resManager->Init("Managers/ResourcesManager.txt");

	// init scenes manager
	sceManager = ScenesManager::GetInstance();
	sceManager->Init("Managers/ScenesManager.txt");

	// FRAME BUFFER
	framebuffer = new Framebuffer();
	framebuffer->Init();
	
}

void Update()
{
	//LOGI("\nFrame time: %f (s)", Timer::FrameTime());
	sceManager->Update();
}

unsigned char pixelData[4];
void Draw()
{
	//
	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->GetFboId());
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	sceManager->DrawWithColor();
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	//

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	sceManager->Draw();

	// OBJECT DETECTION
	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->GetFboId());
	if(im->IsPointerDown())
	{
		glReadPixels(im->GetX(), ScreenSolution::GetInstance()->m_screenHeight - im->GetY(), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelData);
		//printf(" r = %d | g = %d | b = %d | a = %d \n", pixelData[0], pixelData[1], pixelData[2], pixelData[3]);
		unionColor.r = pixelData[0];
		unionColor.g = pixelData[1];
		unionColor.b = pixelData[2];	
		unionColor.a = 0;
		sceManager->ObjectDetection(unionColor.iValue); // object detection
	}
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void Destroy()
{
	ScenesManager::Destroy();
	ResourcesManager::Destroy();
	FileManager::Destroy();
	InputManager::Destroy();
	ScreenSolution::Destroy();

	SAFE_DEL(framebuffer);
}

#ifdef _WIN32
void OnKey(int keyCode, bool isKeyDown)
{
	im->OnKey(keyCode, isKeyDown);			
}


void OnPointer(int type, int x, int y)
{
	if (type == 2) // dragging
	{
		im->OnDrag(x, y);
	}
	else 
	{
		im->OnPointer(type, x, y);
	}
}

int main()
{	
	ScreenSolution::GetInstance()->Init(SCREEN_W, SCREEN_H);
	InitWindow(SCREEN_W, SCREEN_H);
	
	RegisterInitFunc(Init);
	RegisterDrawFunc(Draw);
	RegisterUpdateFunc(Update);	
	RegisterDestroyFunc(Destroy);
	RegisterKeyFunc(OnKey);
	RegisterMouseFunc(OnPointer);

	WinLoop();
	return 0;
}
#else

#ifdef _ANDROID
static void printGLString(const char *name, GLenum s) 
{
    const char *v = (const char *) glGetString(s);
    LOGI("GL %s = %s\n", name, v);
}

static void checkGlError(const char* op) {
    for (GLint error = glGetError(); error; error
            = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}

bool setupGraphics(int w, int h) 
{
    printGLString("Version", GL_VERSION);
    printGLString("Vendor", GL_VENDOR);
    printGLString("Renderer", GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);

    LOGI("setupGraphics(%d, %d)", w, h);
   
    glViewport(0, 0, w, h);
    checkGlError("glViewport");
    return true;
}

extern "C" 
{
    JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_init(JNIEnv * env, jobject obj, jint width, jint height);
    JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_step(JNIEnv * env, jobject obj);
	JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_destroy(JNIEnv * env, jobject obj);
	JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_onKey(JNIEnv * env, jobject obj, jint keyCode, jboolean isKeyDown);
	JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_onPointer(JNIEnv * env, jobject obj, jint type, jint x, jint y);
	JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_onDrag(JNIEnv * env, jobject obj, jint x, jint y);
};


JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_init(JNIEnv * env, jobject obj,  jint width, jint height)
{
	ScreenSolution::GetInstance()->Init(width, height);
	LOGI("%d - %d", ScreenSolution::GetInstance()->m_screenWidth, ScreenSolution::GetInstance()->m_screenHeight);
    setupGraphics(width, height);
	Init();
}

JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_step(JNIEnv * env, jobject obj)
{
	Timer::Tick();
	Update();
	Draw();
}

JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_destroy(JNIEnv * env, jobject obj)
{
	Destroy();
}

JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_onKey(JNIEnv * env, jobject obj, jint keyCode, jboolean isKeyDown)
{
	im->OnKey(keyCode, isKeyDown);
}

JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_onPointer(JNIEnv * env, jobject obj, jint type, jint x, jint y)
{
	im->OnPointer(type, x, y);
}

JNIEXPORT void JNICALL Java_com_android_gl2jni_Application_onDrag(JNIEnv * env, jobject obj, jint x, jint y)
{
	im->OnDrag(x, y);
}
#endif

#endif